package org.springframework.context.annotation;

import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;
import java.util.function.Supplier;

public class AnnotatedBeanDefinitionReader {

	// 这个对象主要的工作就是注册BeanDefinition
	// 那么将BeanDefinition注册到哪里去呢？所以它内部就保存了一个BeanDefinition的注册表。
	// 对应的就是我们代码中的AnnotationConfigApplicationContext
	private final BeanDefinitionRegistry registry;

	// 见名知意，Bean名称的生成器，生成BeanName
	private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

	// 解析@Scope注解
	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

	// 解析@Conditional注解
	private ConditionEvaluator conditionEvaluator;

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
		this(registry, getOrCreateEnvironment(registry));
	}

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		//Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		//Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

	public final BeanDefinitionRegistry getRegistry() {
		return this.registry;
	}

	public void setEnvironment(Environment environment) {
		this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, null);
	}

	public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
		this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
	}

	public void setScopeMetadataResolver(@Nullable ScopeMetadataResolver scopeMetadataResolver) {
		this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
	}

	//注册多个注解Bean定义类
	public void register(Class<?>... annotatedClasses) {
		for (Class<?> annotatedClass : annotatedClasses) {
			registerBean(annotatedClass);
		}
	}

	// 将解析指定的类成为BeanDefinition并注册到容器中
	public void registerBean(Class<?> annotatedClass) {
		doRegisterBean(annotatedClass, null, null, null);
	}

	public <T> void registerBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier) {
		doRegisterBean(annotatedClass, instanceSupplier, null, null);
	}

	public <T> void registerBean(Class<T> annotatedClass, String name, @Nullable Supplier<T> instanceSupplier) {
		doRegisterBean(annotatedClass, instanceSupplier, name, null);
	}

	//Bean定义读取器注册注解Bean定义的入口方法
	@SuppressWarnings("unchecked")
	public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {
		doRegisterBean(annotatedClass, null, null, qualifiers);
	}

	//Bean定义读取器向容器注册注解Bean定义类
	@SuppressWarnings("unchecked")
	public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
		doRegisterBean(annotatedClass, null, name, qualifiers);
	}

	//Bean定义读取器向容器注册注解Bean定义类
	<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
		// Spring在这里写死了，直接new了一个AnnotatedGenericBeanDefinition，也就是说通过reader对象注册的BeanDefinition都是AnnotatedGenericBeanDefinition。
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);

		// 调用conditionEvaluator的shouldSkip方法，判断当前的这个bd是否需要被注册
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		// 在注册时可以提供一个instanceSupplier
		abd.setInstanceSupplier(instanceSupplier);

		//解析@Scope注解，得到一个ScopeMetadata
		//解析注解Bean定义的作用域，若@Scope("prototype")，则Bean为原型类型；若@Scope("singleton")，则Bean为单态类型
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);

		// 将@Scope注解中的信息保存到bd中
		abd.setScope(scopeMetadata.getScopeName());

		// 调用beanNameGenerator生成beanName
		// 所谓的注册bd就是指定将bd放入到容器中的一个beanDefinitionMap中
		// 其中的key就是beanName,value就是解析class后得到的bd
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		// 这句代码将进一步解析class上的注解信息，Spring在创建这个abd的信息时候就已经将当前的class放入其中了，
		// 所有这行代码主要做的就是通过class对象获取到上面的注解（包括@Lazy，@Primary，@DependsOn注解等等），
		// 然后将得到注解中对应的配置信息并放入到bd中的属性中
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

		//如果在向容器注册注解Bean定义时，使用了额外的限定符注解，则解析限定符注解。
		//主要是配置的关于autowiring自动依赖注入装配的限定条件，即@Qualifier注解
		//Spring自动依赖注入装配默认是按类型装配，如果使用@Qualifier则按名称

		// 正常的容器启动阶段qualifiers肯定等于null
		// 我能想到的不为空的方法就是直接在外部调用了register方法并且出入了qualifiers参数
		// 就是说我们手动直接注册了一个类，但是我们没有在类上添加@Lazy，@Primary注解，但是我们又希望能将其标记为Primary为true/LazyInit为true,这个时候就手动传入Primary.class跟Lazy.class即可。
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				//如果配置了@Primary注解，设置该Bean为autowiring自动依赖注入装//配时的首选
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				//如果配置了@Lazy注解，则设置该Bean为非延迟初始化，如果没有配置，
				//则该Bean为预实例化
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				//如果使用了除@Primary和@Lazy以外的其他注解，则为该Bean添加一
				//个autowiring自动依赖注入装配限定符，该Bean在进autowiring
				//自动依赖注入装配时，根据名称装配限定符指定的Bean
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}

		// 我们注册时，我们可以传入一些回调方法，在解析得到bd后调用
		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}

		//创建一个指定Bean名称的Bean定义对象，封装注解Bean定义类数据
		// bd中是没有beanName属性的，BeanDefinitionHolder中就是保存了beanName以及对应的BeanDefinition
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

		// 这个地方主要是解析Scope中的ProxyMode属性，默认为no，不生成代理对象
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

		// 注册bd到容器中，实际上最终就是将bd放到了beanFactory中的一个map里（beanDefinitionMap）
		// key为beanName,value为bd
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

	private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry instanceof EnvironmentCapable) {
			return ((EnvironmentCapable) registry).getEnvironment();
		}
		return new StandardEnvironment();
	}
}
